package com.yz.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yz.model.dto.order.PayOrderRequest;
import com.yz.model.vo.order.OrderVo;
import com.yz.model.vo.promotion.PromotionVo;
import com.yz.common.constant.PromotionConstant;
import com.yz.common.factory.JedisFactory;
import com.yz.common.resp.BaseResponse;
import com.yz.order.mapper.OrderMapper;
import com.yz.order.mapping.OrderMapping;
import com.yz.model.entity.order.Order;
import com.yz.order.service.OrderService;
import com.yz.order.service.feign.OrderPayFeign;
import com.yz.order.service.feign.OrderPromotionFeign;
import com.yz.model.entity.promotion.PromotionCode;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;

import java.math.BigDecimal;
import java.util.Random;

import static com.yz.common.utils.PromotionCodeUtils.getPromotionInfo;

/**
 * Created With IntelliJ IDEA.
 * Descriptions:
 * <p>
 * User:Mr.Du
 * Date:2024/4/9
 * Time:10:27
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Resource
    private OrderMapping orderMapping;

    @Resource
    private OrderPromotionFeign orderPromotionFeign;

    @Resource
    private JedisFactory jedisFactory;

    @Resource
    private OrderPayFeign orderPayFeign;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public OrderVo returnOrderInfo(Long uid, Long amount, String codes) {
        if(codes != null && (codes.length() != 49 || (codes.charAt(codes.length() - 1) != '0' && codes.charAt(codes.length() - 1) != '1'))){
            throw new RuntimeException("优惠码格式错误");
        }
        Jedis jedis = jedisFactory.getJedis();
        try{
            Float discountRate = null;
            BigDecimal realAmount = null;
            BigDecimal amounts = new BigDecimal(amount.toString());
            if(StringUtils.isNoneBlank(codes)){
                // 校验优惠码
                BaseResponse<PromotionVo> baseResponse = orderPromotionFeign.checkPromotionCode(uid, codes);
                if(baseResponse.getCode() != 0){
                    throw new RuntimeException("优惠码不可用");
                }
                discountRate = baseResponse.getData().getDiscountRate();
            }
            if(discountRate == null){
                realAmount = amounts;
            }else {
                BigDecimal discount = new BigDecimal(discountRate.toString());
                realAmount = amounts.multiply(BigDecimal.ONE.subtract(discount));
            }
            realAmount = realAmount.setScale(2, BigDecimal.ROUND_HALF_UP);
            // 生成订单info
            OrderVo orderVo = OrderVo.builder()
                    .subject("购买憨币")
                    .amount(amount)
                    .realAmount(realAmount.floatValue())
                    .discountRate(discountRate)
                    .codes(codes)
                    .build();
            log.info("订单信息:{}", orderVo);
            return orderVo;
        }catch (Exception e){
            throw new RuntimeException("获取订单信息失败");
        }
    }

    @Override
    @Transactional
    public String orderReCharge(Long uid, PayOrderRequest payOrderRequest) {
            Jedis jedis = jedisFactory.getJedis();
            String codes = "";
            PromotionCode promotionCode = PromotionCode.builder().build();
            if(StringUtils.isNoneBlank(payOrderRequest.getCodes())){
                codes = payOrderRequest.getCodes();
                // 校验验证码
                BaseResponse<PromotionVo> baseResponse = orderPromotionFeign.checkPromotionCode(uid, codes);
                if(baseResponse.getCode() != 0){
                    log.error("优惠码不可用");
                    throw new RuntimeException("优惠码不可用");
                }
                String key  = PromotionConstant.PROMOTION_CODE_PREFIX + uid + codes;
                // 使用分布式锁，确保一个优惠码同一时间只能有一个用户使用
                RLock lock = redissonClient.getLock(key+"lock");
                try {
                    boolean flag = lock.tryLock();
                    if (!flag) {
                        throw new RuntimeException("使用失败，请勿重复操作");
                    }
                    // 再次校验优惠码是否使用
                    String cache_codes = jedis.get(key);
                    if(StringUtils.isNotEmpty(cache_codes)){
                        throw new RuntimeException("优惠码不可用");
                    }
                    // 没使用，就保存，同时设置过期时间，时间设置为支付超时时间。
                    jedis.setex(PromotionConstant.PROMOTION_CODE_PREFIX + uid + codes, PromotionConstant.LOCK_TIMEOUT, codes);
                    String[] promotionInfo = getPromotionInfo(codes);
                    String code = promotionInfo[0];
                    String token = promotionInfo[1];
                    Integer type = Integer.valueOf(promotionInfo[2]);
                    promotionCode = PromotionCode.builder()
                            .code(code).token(token).type(type).build();
                }catch (Exception e){
                    throw new RuntimeException(e.getMessage());
                }finally {
                    // 释放锁
                    lock.unlock();
                }
            }
            Order order = Order.builder()
                        .subject(payOrderRequest.getSubject()).uid(uid)
                        .amount(payOrderRequest.getAmount()).status(0)
                        .realAmount(payOrderRequest.getRealAmount())
                        .discountRate(payOrderRequest.getDiscountRate())
                        .promotionCode(JSON.toJSONString(promotionCode)).build();
            // 保存订单
            baseMapper.insert(order);
            PayOrderRequest pay = new PayOrderRequest();
            BeanUtils.copyProperties(order, pay);
            // 支付
            log.info("支付信息:{}", order);
            // 获取支付页面url
            BaseResponse<String> stringBaseResponse = orderPayFeign.reCharge(uid, pay);
            if(stringBaseResponse.getCode() != 0){
                throw new RuntimeException("支付失败");
            }
            return stringBaseResponse.getData();
    }
}
